/////////////////////////////////////////////////
//
// Autor: Arkadiusz Kiciak, Gdansk Poland 15.05.2013R
//
/////////////////////////////////////////////////
//
// License: GPLV3
//
/////////////////////////////////////////////////
//
// Feel free to redistributing this code
// but never forget to add original place
// where you found this ( url target ) Thanks!!!
//
/////////////////////////////////////////////////
//
// Software PWM driver for RGB diode (common anode)
//
// Items of rgb color are randomized in every counter interrupt  
// In the middle of main interrupt color is smooth changing from
// before color to the next
//
// Expeccted current od one output is: 22mA
// Current sum for one RGB diode is: 3 * 22mA -> 66mA
// 
// Permitted current for one outpus is: 40mA
// Permitted current for entire chip is: 200mA
//
// Makefile change:
// > fusebits was set for external 16MHz quartz
// > debug secion was striped out
// > as preferred chip was set ATmega32
// > as preferred programmer was set USBasp / usb
//
/////////////////////////////////////////////////
//
// > "avr-objdump.exe" -h main.elf
//
// main.elf:     file format elf32-avr
//
// Sections:
// Idx Name          Size      VMA       LMA       File off  Algn
//   0 .text         0000085a  00000000  00000000  00000094  2**1
//                   CONTENTS, ALLOC, LOAD, READONLY, CODE
//   1 .data         00000004  00800060  0000085a  000008ee  2**0
//                   CONTENTS, ALLOC, LOAD, DATA
//   2 .bss          0000001c  00800064  00800064  000008f2  2**0
//                   ALLOC
//
// > Process Exit Code: 0
// > Time Taken: 00:00
//
/////////////////////////////////////////////////

#include <stdlib.h>
#include <math.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/io.h>

#define R1_ORDER		PA0
#define R1_DDR			DDRA
#define R1_OUT			PORTA
#define setR1AsOutput() ( R1_DDR |= _BV(R1_ORDER) )

#define G1_ORDER		PA1
#define G1_DDR			DDRA
#define G1_OUT			PORTA
#define setG1AsOutput() ( G1_DDR |= _BV(G1_ORDER) )

#define B1_ORDER		PA2
#define B1_DDR			DDRA
#define B1_OUT			PORTA
#define setB1AsOutput() ( B1_DDR |= _BV(B1_ORDER) )

typedef struct {

	unsigned char r;
	unsigned char g;
	unsigned char b;

} rgbCont;

typedef struct {

	short r;
	short g;
	short b;
	
} rgbDiffCont;

typedef struct {

	char r;
	char g;
	char b;
	
} lPState;

rgbCont rgbState, rgbCurrent, rgbBefore;

rgbDiffCont rgbDiff;

lPState lastPinoutState;

unsigned char interruptCounter;

unsigned int randomSeed;

void setRandomRGBAt(rgbCont *prgb){

	srand(randomSeed);

	(*prgb).r = rand() % 256;
	
	(*prgb).g = rand() % 256;

	(*prgb).b = rand() % 256;

}

void setPinoutDependOf(unsigned char *preg, unsigned char bitOrder, char dependOf){

	if(dependOf)
	
		(*preg) |= _BV(bitOrder);

	else
	
		(*preg) &= ~_BV(bitOrder);

}

void setCounterInitParam(void){

	TCCR0 = _BV(CS00);
	
	TCNT0 = 0x00;
	
	TIMSK = _BV(TOIE0);

}

void setPWMOutputs(void){

	setR1AsOutput();

	setG1AsOutput();

	setB1AsOutput();

}

void softDriverPWM(void){

	static unsigned char interruptCounter = 0;
	
	interruptCounter++;
	
	char boolStatment;

	boolStatment = (interruptCounter <= rgbCurrent.r);

	if(boolStatment != lastPinoutState.r){
	
		setPinoutDependOf((unsigned char *)&R1_OUT,R1_ORDER,boolStatment);

		lastPinoutState.r = boolStatment;

	}
	
	boolStatment = (interruptCounter <= rgbCurrent.g);

	if(boolStatment != lastPinoutState.g){
	
		setPinoutDependOf((unsigned char *)&G1_OUT,G1_ORDER,boolStatment);
	
		lastPinoutState.g = boolStatment;

	}

	boolStatment = (interruptCounter <= rgbCurrent.b);

	if(boolStatment != lastPinoutState.b){
	
		setPinoutDependOf((unsigned char *)&B1_OUT,B1_ORDER,boolStatment);

		lastPinoutState.b = boolStatment;
	
	}

}

ISR (TIMER0_OVF_vect) {

	randomSeed++;
	
	static unsigned int randomCounter = 0;
	static unsigned int randomCounter2 = 0;
		
	static unsigned int additionCounter;
		
	if(randomCounter >= 30720){
	
		randomCounter2++;
		
		randomCounter = 0;
				
	}
	
	randomCounter++;

	if(randomCounter2 > 4){
	
		randomCounter2 = 0;
		
		randomCounter  = 0;
		
		setRandomRGBAt(&rgbState);
		
		rgbDiff.r = rgbState.r - rgbCurrent.r;
		rgbDiff.g = rgbState.g - rgbCurrent.g;
		rgbDiff.b = rgbState.b - rgbCurrent.b;
	
		rgbBefore = rgbCurrent;
		
		//expensive floating foint operations
		//are making only ones
		float stepValue = 1000 / 30720.0;
	
		rgbDiff.r *=  stepValue;
		rgbDiff.g *=  stepValue;
		rgbDiff.b *=  stepValue;
		
		additionCounter = 31;
		
	}
	
	
	if(!(randomCounter % 1000) && additionCounter){
	
		additionCounter--;

		//in the middle of algorithm
		//only simple arithmetic operations
		rgbCurrent.r += rgbDiff.r;
		rgbCurrent.g += rgbDiff.g;
		rgbCurrent.b += rgbDiff.b;
		
	}
	
	softDriverPWM();

}

int main(void){
	
	setPWMOutputs();

	setCounterInitParam();
		
	randomSeed = 0;
	
	lastPinoutState.r = 0;
	lastPinoutState.g = 0;
	lastPinoutState.b = 0;

	setRandomRGBAt(&rgbCurrent);
	
	softDriverPWM();
	
	sei();
		
	while(1);
	
	return 0;
	
}